package com.ithinks.flow.common.utils.protocol.socket.aio.client;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.CountDownLatch;

/**
 * @Description
 * @ClassName AioHandler
 * @Author MR.LI
 * @date 2020.12.07 18:03
 */
public class AioHandler implements CompletionHandler<Void,AioClient>,Runnable {

    private AsynchronousSocketChannel clientChannel;

    private String host;

    private int port;

    //防止线程退出
    private CountDownLatch latch;

    public AioHandler(String host,int port){
        this.host = host;
        this.port = port;
        try {
            //创建一个实际异步得客户端通道
            clientChannel  = AsynchronousSocketChannel.open();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        //因为是异步调用，connect不会阻塞，run方法迅速结束，负责网络通讯得线程也会结束.
        latch = new CountDownLatch(1);

        clientChannel.connect(new InetSocketAddress(host,port),null,this);
        try {
            latch.await();
            clientChannel.close();

        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //如果连接成功，会回调这个实体得completed方法
    @Override
    public void completed(Void result, AioClient attachment) {
        System.out.println("已经连接到服务端。");
    }

    //连接失败，这个方法会被系统调用
    @Override
    public void failed(Throwable exc, AioClient attachment) {
        System.err.println("连接失败。");
        exc.printStackTrace();
        latch.countDown();
        try {
            clientChannel.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //对外暴露服务端发送数据得API
    public void sendMessage(String msg){
        byte[] bytes = msg.getBytes();
        ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
        writeBuffer.put(bytes);
        writeBuffer.flip();
        /*进行异步写，同样的这个方法会迅速返回，
        需要提供一个接口让系统在一次网络写操作完成后通知我们的应用程序。
        所以我们传入一个实现了CompletionHandler的AioClientWriteHandler
        第1个writeBuffer，表示我们要发送给服务器的数据；
        第2个writeBuffer，考虑到网络写有可能无法一次性将数据写完，需要进行多次网络写，
        所以将writeBuffer作为附件传递给AioClientWriteHandler。
        */
        clientChannel.write(writeBuffer,writeBuffer,new AioWriterHandler(clientChannel,latch));
    }
}
